/** \file lp_app.c
 * \brief Loader for the gladefile containing the LogicParser GTK+ gui.
 * \author Marcelo Lira dos Santos
 * \date 2006-2007
 *
 * This is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License, see the file LICENSE.
 */
#include "lp_app.h"

/** \brief Initializes the LogicParser application.
 * Loads the ui from the glade file, create the data models and connect the
 * callbacks.
 * \param gladefile The path of the glade ui file.
 * \return A LpApp structure containting the loaded gui.
 */
LpApp *
lp_app_init (gchar *gladefile)
{
	LpApp *lpApp = NULL;

	lpApp = g_new0(LpApp, 1);
	lpApp->widgetTree = glade_xml_new (gladefile, NULL, NULL);
	
	// Load Widgets
	lpApp->lpWindow = glade_xml_get_widget(lpApp->widgetTree, "LPWindow");
	lpApp->exprEntry = glade_xml_get_widget(lpApp->widgetTree, "exprEntry");
	lpApp->parsedTree = glade_xml_get_widget(lpApp->widgetTree, "parsedTree");
	lpApp->graphText = glade_xml_get_widget(lpApp->widgetTree, "graphText");
	lpApp->graphImage = glade_xml_get_widget(lpApp->widgetTree, "graphImage");
	lpApp->dotToolbar = glade_xml_get_widget(lpApp->widgetTree, "dot_toolbar");
	lpApp->imageToolbar = glade_xml_get_widget(lpApp->widgetTree, "image_toolbar");
	
	gtk_window_resize (GTK_WINDOW(lpApp->lpWindow), 600, 500);
	
	// Create data model
	lp_app_init_tree (lpApp);
	lp_app_init_text (lpApp);
	
	// Connect callbacks
	glade_xml_signal_connect (lpApp->widgetTree, "quit_cb",
										GTK_SIGNAL_FUNC (quit_cb));
	glade_xml_signal_connect_data (lpApp->widgetTree, "parse_cb",
											GTK_SIGNAL_FUNC (parse_cb),
											LPAPP_TO_GPOINTER(lpApp));
	glade_xml_signal_connect_data (lpApp->widgetTree, "save_image_cb",
											GTK_SIGNAL_FUNC (save_image_cb),
											LPAPP_TO_GPOINTER(lpApp));
	glade_xml_signal_connect_data (lpApp->widgetTree, "save_dot_cb",
											GTK_SIGNAL_FUNC (save_dot_cb),
											LPAPP_TO_GPOINTER(lpApp));
	glade_xml_signal_connect_data (lpApp->widgetTree, "zoom_in_cb",
											GTK_SIGNAL_FUNC (zoom_in_cb),
											LPAPP_TO_GPOINTER(lpApp));
	glade_xml_signal_connect_data (lpApp->widgetTree, "zoom_out_cb",
											GTK_SIGNAL_FUNC (zoom_out_cb),
											LPAPP_TO_GPOINTER(lpApp));
	glade_xml_signal_connect_data (lpApp->widgetTree, "normal_view_cb",
											GTK_SIGNAL_FUNC (normal_view_cb),
											LPAPP_TO_GPOINTER(lpApp));

	return lpApp;
}

/** \brief Show the application main window and enter the main loop.
 * \param lpApp A LpApp structure containting the gui and its components.
 */
void
lp_app_run (LpApp *lpApp)
{
	gtk_widget_show_all(lpApp->lpWindow);
	gtk_main ();
}

/** \brief Initializes the TreeView and its data model.
 * \param lpApp A LpApp structure containting the gui and its components.
 */
void
lp_app_init_tree (LpApp *lpApp)
{
	GtkTreeViewColumn *column;
	GtkCellRenderer *renderer;
	
	lpApp->parsedTreeModel = gtk_tree_store_new (1, G_TYPE_STRING);
	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes (
									"Parsed Tree",
									renderer,
									"text", 0,
									NULL);
	gtk_tree_view_append_column (GTK_TREE_VIEW(lpApp->parsedTree), column);	
	gtk_tree_view_set_model(GTK_TREE_VIEW(lpApp->parsedTree),
									GTK_TREE_MODEL(lpApp->parsedTreeModel));
}

/** \brief Initializes the TextView and its data model.
 * \param lpApp A LpApp structure containting the gui and its components.
 */
void
lp_app_init_text (LpApp *lpApp)
{
	PangoFontDescription *font;

	font = pango_font_description_from_string ("Mono 12");
	gtk_widget_modify_font (lpApp->graphText, font);
	pango_font_description_free (font);

	lpApp->graphTextBuffer = gtk_text_buffer_new (NULL);
	gtk_text_view_set_buffer (GTK_TEXT_VIEW(lpApp->graphText),
										lpApp->graphTextBuffer);
}

/** \brief Updates the TreeView with the parsed tree or an error message.
 * \param lpApp A LpApp structure containting the gui and its components.
 * \param root A LpNode pointing to the root of the parsed expression tree.
 */
void
lp_app_update_tree (LpApp *lpApp, LpNode *root)
{
	gtk_tree_store_clear (lpApp->parsedTreeModel);
	lp_app_add_node (lpApp->parsedTreeModel, NULL, root);
	gtk_tree_view_expand_all (GTK_TREE_VIEW(lpApp->parsedTree));
}

/** \brief Append a note of the parsed tree to the TreeView.
 * \param parsedTreeModel Data model for the TreeView.
 * \param parent GtkTreeModel iterator representing the parent row.
 * \param node A LpNode pointing to the root of the parsed expression tree.
 */
void
lp_app_add_node (GtkTreeStore *parsedTreeModel, 
						GtkTreeIter *parent,
						LpNode *node)
{
	gchar *node_text;
	GtkTreeIter iter;

	if (node != NULL) {
		if (node->lpToken->nodeType == C_ID) {
			node_text = g_strdup_printf("%c%d",
											node->lpToken->nodeType,
											node->lpToken->nodeId);
			gtk_tree_store_append (parsedTreeModel, &iter, parent);
			gtk_tree_store_set (parsedTreeModel, &iter,
										0, node_text,
               		      	-1);
		} else if (node->lpToken->nodeType == C_IF) {
			node_text = g_strdup_printf("->");
			gtk_tree_store_append (parsedTreeModel, &iter, parent);
			gtk_tree_store_set (parsedTreeModel, &iter, 0, node_text, -1);
		} else {
			node_text = g_strdup_printf("%c", node->lpToken->nodeType);
			gtk_tree_store_append (parsedTreeModel, &iter, parent);
			gtk_tree_store_set (parsedTreeModel, &iter, 0, node_text, -1);
		}

		lp_app_add_node (parsedTreeModel, &iter, node->left);
		lp_app_add_node (parsedTreeModel, &iter, node->right);
	}
}

/** \brief Append a note of the parsed tree to the TreeView.
 * \param lpApp A LpApp structure containting the gui and its components.
 * \param root A LpNode pointing to the root of the parsed expression tree.
 */
void
lp_app_update_graph (LpApp *lpApp, LpNode *root)
{
	gchar *graph_img_file = "./graph.png";
	gchar *dotgraph = lp_graph_generate (root);
	
	gtk_text_buffer_set_text (lpApp->graphTextBuffer, dotgraph, -1);
	
	if (lp_graph_save_as_image (dotgraph, graph_img_file)) {
		gtk_image_set_from_file(GTK_IMAGE(lpApp->graphImage), graph_img_file);
	}
}

gchar *
save_file (GtkWindow *parent_window, gchar *type)
{
	GtkWidget *dialog;
	GtkFileFilter *filter;
	gchar *filename = NULL;
	gchar *pattern = g_strconcat("*.", type, NULL);;
	gchar *name_suggest = g_strconcat("unnamed.", type, NULL);
	
	dialog = gtk_file_chooser_dialog_new ("Save File",
														parent_window,
														GTK_FILE_CHOOSER_ACTION_SAVE,
														GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
														GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
														NULL);
	gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);

	filter = gtk_file_filter_new();
	gtk_file_filter_add_pattern (filter, pattern);
	gtk_file_chooser_set_filter(GTK_FILE_CHOOSER (dialog), filter);
	gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER (dialog),
													g_get_home_dir());
	gtk_file_chooser_set_filter(GTK_FILE_CHOOSER (dialog), filter);
	gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), name_suggest);

	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
		filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));

	gtk_widget_destroy (dialog);

	return filename;
}

// Callbacks ------------------------------------------------------------------

/** \brief Quits the application.
 * \param widget The widget that generated the signal.
 * \param e A GdkEventKey.
 */
void
quit_cb (GtkWidget *widget, GdkEventKey *e)
{
	gtk_main_quit();
}

/** \brief Parse the expression in the exprEntry text entry.
 * \param widget The widget that generated the signal.
 * \param data A gpointer to an LpApp structure.
 */
void
parse_cb (GtkWidget *widget, gpointer data)
{
	gchar *expr;
	LpNode *root;
	LpApp *lpApp = GPOINTER_TO_LPAPP(data);
	
	gtk_widget_set_sensitive(lpApp->dotToolbar, TRUE);
	gtk_widget_set_sensitive(lpApp->imageToolbar, TRUE);

	expr = g_ascii_strup (gtk_entry_get_text (GTK_ENTRY(lpApp->exprEntry)),
								-1);

	root = lp_parse_expression (expr);
	lp_app_update_tree (lpApp, root);
	lp_app_update_graph (lpApp, root);

}

/** \brief Save the image generated by GraphViz for the parsed tree.
 * \param widget The widget that generated the signal.
 * \param data A gpointer to an LpApp structure.
 */
void
save_image_cb (GtkWidget *widget, gpointer data)
{
	LpApp *lpApp = GPOINTER_TO_LPAPP(data);
	FILE *img_file;
	gchar *filename;

	filename = save_file(GTK_WINDOW(lpApp->lpWindow), "png");

	if (filename != NULL) {
		gdk_pixbuf_savev (gtk_image_get_pixbuf(GTK_IMAGE(lpApp->graphImage)),
								filename, "png", NULL, NULL, NULL);
		g_free(filename);
	}
}

/** \brief Save the GraphViz's dot file for the parsed tree.
 * \param widget The widget that generated the signal.
 * \param data A gpointer to an LpApp structure.
 */
void
save_dot_cb (GtkWidget *widget, gpointer data)
{
	LpApp *lpApp = GPOINTER_TO_LPAPP(data);
	FILE *dot_file;
	gchar *filename;
	gchar *graph_text;
	GtkTextIter start;
	GtkTextIter end;

	filename = save_file(GTK_WINDOW(lpApp->lpWindow), "dot");

	if (filename != NULL) {
		gtk_text_buffer_get_bounds (lpApp->graphTextBuffer, &start, &end);
		graph_text = gtk_text_buffer_get_text (lpApp->graphTextBuffer,
															&start, &end, FALSE);

		dot_file = g_fopen(filename, "w");
		g_fprintf(dot_file, "%s", graph_text);
		close(dot_file);

		g_free(filename);
	}
}

/** \brief Zoom in the graph of the parsed tree.
 * \param widget The widget that generated the signal.
 * \param data A gpointer to an LpApp structure.
 */
void
zoom_in_cb (GtkWidget *widget, gpointer data)
{
	g_print("zoom_in_cb\n");
}

/** \brief Zoom out the graph of the parsed tree.
 * \param widget The widget that generated the signal.
 * \param data A gpointer to an LpApp structure.
 */
void
zoom_out_cb (GtkWidget *widget, gpointer data)
{
	g_print("zoom_out_cb\n");
}

/** \brief Show the normal zoom of the graph of the parsed tree.
 * \param widget The widget that generated the signal.
 * \param data A gpointer to an LpApp structure.
 */
void
normal_view_cb (GtkWidget *widget, gpointer data)
{
	g_print("normal_view_cb\n");
}
